#include <iostream>

extern "C" {
#include <libavformat/avformat.h>
}

int main(int argc, char *argv[])
{
    std::string defaultFile = "believe.mp4";
    std::string inputFile;

    if (argv[1] == NULL)
        inputFile = defaultFile;
    else
        inputFile = std::string(argv[1]);

    std::cout << "Input file: " << inputFile << std::endl;

    //AVFormatContext 是描述一个媒体文件或媒体流的构成和基本信息的结构体
    AVFormatContext *inputFmtCtx = NULL;
    int videoIndex = -1; //视频流索引
    int audioIndex = -1; //音频索引
    int packetCount = 0;
    int printMaxCount = 10;//读取10帧用于测试
    AVPacket *packet = av_packet_alloc();

    //打开文件，探测协议类型，如果是网络文件则创建网络连接
    int ret = avformat_open_input(&inputFmtCtx, inputFile.c_str(), NULL, NULL);
    if (ret < 0) {
        char errorBuf[1024] = {0};
        //获取错误码，成功则返回0，如果无法找到对应的错误码，则返回负数
        av_strerror(ret, errorBuf, sizeof(errorBuf)-1);
        std::cout << "Failed to open input file. Error: " << errorBuf << std::endl;
        goto failed_deal;
    }

    ret = avformat_find_stream_info(inputFmtCtx, NULL);
    if (ret < 0) {
        char errorBuf[1024] = {0};
        av_strerror(ret, errorBuf, sizeof(errorBuf)-1);
        std::cout << "Failed to find stream info. Error: " << errorBuf << std::endl;
        goto failed_deal;
    }

    //成功打开媒体文件
    std::cout << "===== av_dump_format =====" << std::endl;
    av_dump_format(inputFmtCtx, 0, inputFile.c_str(), 0);
    std::cout << "===== av_dump_finished =====" << std::endl;

    //url:调用 avformat_open_intput 读取到媒体文件的路径/名字
    std::cout << "media name: " << inputFmtCtx->url << std::endl;

    //nb_streams:媒体流数量
    std::cout << "stream number: " << inputFmtCtx->nb_streams << std::endl;

    //bit_rate:媒体文件的码率，单位为 bps
    std::cout << "media average ratio: " << (int64_t)(inputFmtCtx->bit_rate/1024) << " kbps" << std::endl;

    //媒体文件的时间信息
    int totalSeconds, hour, minute, second;
    //duration 的默认单位为 us,除以 AV_TIME_BASE 转化为 s
    totalSeconds = (inputFmtCtx->duration) / AV_TIME_BASE;
    hour = totalSeconds / 60 / 60;
    minute = (totalSeconds / 60) % 60;
    second = totalSeconds % 60;
    std::cout << "total duration: " << hour << ":" << minute << ":" << second << std::endl;

    //老版本通过遍历读取媒体文件中视频和音频的信息
    //新版本可以使用 av_find_best_stream 读取
    for (uint32_t i = 0; i < inputFmtCtx->nb_streams; i++) {
        //获取对应的流:音频流、视频流、字幕流
        AVStream *inputStream = inputFmtCtx->streams[i];

        switch (inputStream->codecpar->codec_type) {
        case AVMEDIA_TYPE_AUDIO: {
            std::cout << "----- Audio info:" << std::endl;
            //index:每个流成分在 ffmpeg 解复用分析后都有唯一的 index 作为标识
            std::cout << "index: " << inputStream->index << std::endl;
            //sample_rate:音频编解码器的采样率，单位为 Hz
            std::cout << "sampleRate: " << inputStream->codecpar->sample_rate << "Hz" << std::endl;

            switch (inputStream->codecpar->format) {
            case AV_SAMPLE_FMT_FLTP:
                std::cout << "Sample format: AV_SAMPLE_FMT_FLTP" << std::endl;
                break;
            case AV_SAMPLE_FMT_S16P:
                std::cout << "Sample format: AV_SAMPLE_FMT_S16P" << std::endl;
                break;
            default:
                std::cout << "Other format. Format type: " << inputStream->codecpar->format << std::endl;
                break;
            }
            //channels:音频信道数目
            std::cout << "channel number: " << inputStream->codecpar->channels << std::endl;
            //codec_id:音频压缩编码格式
            switch (inputStream->codecpar->codec_id) {
            case AV_CODEC_ID_AAC:
                std::cout << "Audio codec: AAC" << std::endl;
                break;
            case AV_CODEC_ID_MP3:
                std::cout << "Audio codec: MP3" << std::endl;
                break;
            default:
                std::cout << "Other codec_id: " << inputStream->codecpar->codec_id << std::endl;
                break;
            }

            //音频总时长，单位为s。注意:如果把单位放大为ms或者us，音频总时长和视频总时长不一定相等
            if (inputStream->duration != AV_NOPTS_VALUE) {
                int audioDuration = (inputStream->duration) * av_q2d(inputStream->time_base);
                std::cout << "audio duration: " << audioDuration/3600 << ":" << (audioDuration/60)%60 << ":" << audioDuration % 60 << std::endl;
            }
            else
                std::cout << "audio duration unkown." << std::endl;

            audioIndex = i;
        }
            break;
        case AVMEDIA_TYPE_VIDEO: {
            std::cout << "-----Video info:" << std::endl;
            std::cout << "index: " << inputStream->index;

            //avg_frame_rate:视频帧率，单位为 fps，表示每秒多少帧
            std::cout << av_q2d(inputStream->avg_frame_rate) << " fps" << std::endl;

            switch (inputStream->codecpar->codec_id) {
            case AV_CODEC_ID_MPEG4:
                std::cout << "video codec: MPEG4" << std::endl;
                break;
            case AV_CODEC_ID_H264:
                std::cout << "video codec: H264" << std::endl;
                break;
            default:
                std::cout << "video codec_id: " << inputStream->codecpar->codec_id << std::endl;
                break;
            }

            //视频帧的宽度和高度
            std::cout << "width: " << inputStream->codecpar->width << " heigth: " << inputStream->codecpar->height << std::endl;

            //视频总时长，单位为s。注意:如果把单位放大为ms或us，音频总时长和视频总时长不一定相等
            if (inputStream->duration != AV_NOPTS_VALUE) {
                int videoDuration = (inputStream->duration) * av_q2d(inputStream->time_base);
                std::cout << "video duration: " << (videoDuration / 3600) << ":" << (videoDuration/60) % 60 << ":" << (videoDuration%60) << std::endl;
            }
            else
                std::cout << "video duration unknown." << std::endl;

            videoIndex = i;
        }
            break;
        case AVMEDIA_TYPE_SUBTITLE:
            break;
        default:
            std::cout << "Other streams. Stream type: " << inputStream->codecpar->codec_type << std::endl;
            break;
        }
    }

    std::cout << "----- av_read_frame start -----" << std::endl;

    while (true) {
        ret = av_read_frame(inputFmtCtx, packet);
        if (ret < 0) {
            std::cout << "av_read_frame end" << std::endl;
            break;
        }

        if (packetCount++ < printMaxCount) {
            if (packet->stream_index == audioIndex) {
                std::cout << "audio pts: "  << packet->pts << std::endl;
                std::cout << "audio dts: "  << packet->dts << std::endl;
                std::cout << "audio size: " << packet->size << std::endl;
                std::cout << "audio pos: "  << packet->pos << std::endl;
                std::cout << "audio duration: " << packet->duration * av_q2d(inputFmtCtx->streams[audioIndex]->time_base) << std::endl << std::endl;
            }
            else if (packet->stream_index == videoIndex) {
                std::cout << "video pts: "  << packet->pts << std::endl;
                std::cout << "video dts: "  << packet->dts << std::endl;
                std::cout << "video size: " << packet->size << std::endl;
                std::cout << "video pos: "  << packet->pos << std::endl;
                std::cout << "video duration: " << packet->duration * av_q2d(inputFmtCtx->streams[videoIndex]->time_base) << std::endl << std::endl;
            }
            else
                std::cout << "Unknown stream_index: " << packet->stream_index << std::endl;
        }
    }

    if (packet != NULL)
        av_packet_free(&packet);

failed_deal:
    if (inputFmtCtx != NULL)
        avformat_close_input(&inputFmtCtx);

    system("pause");

    return 0;
}
